import Backboard from "./backboard";
import {
  getReal,
  getThreeBezierPoint,
  compare,
  lineEquation,
  distanceToLine,
  getCircularPos,
} from "../common.js";

// ***************** 添加图形的选中标记 *****************
// 主要用于添加 缩放框 和 连接点
export default class Sign extends Backboard {
  constructor() {
    super();
    this.sign = [];
    this.linkArr = []; // 存放所有图形的连接线标记点
    this.mouseX = 0;
    this.mouseY = 0;
    this.subX = 0;
    this.subY = 0;
    this.originX = 0; // 记录中心点
    this.originY = 0;
    this.dragStartX = 0;
    this.dragStartY = 0;
    this.mouseRelativeX = 0; // 记录鼠标离图形中心的相对位置（方便计算旋转角）
    this.mouseRelativeY = 0;
    this.signWidth = 8;
    this.signHeight = 8;
    this.startRotate = 0;
    this.endRotate = 0;
    this.PropAboutX = ["x", "endX", "minX", "maxX"];
    this.PropAboutY = ["y", "endY", "minY", "maxY"];
    this.timeout = null;
    this.highlightEle = "";
    this.idMap = new Map();

    this.r = 0;
    this.g = 0;
  }

  // 获取图案id
  getUniqueId(nodeId) {
    let id = nodeId || new Date().getTime();
    return this.getId(id);
  }
  getId(id) {
    if (!this.idMap.get(id)) {
      this.idMap.set(id, true);
      return id;
    } else {
      id += 1;
      return this.getId(id);
    }
  }

  // 判断当前鼠标点击位置是否在曲线周围
  // 将曲线分割成十份
  // curve: 曲线信息       clickPos: 鼠标点击位置 {x, y}
  judgeCurve(curve, clickPos) {
    let { x, y, x1, y1, x2, y2, endX, endY } = curve;
    let range = [[x, y]]; // 将曲线平均分成十段后，每个点的坐标（ 中间插入9个点 ）
    for (let i = 0.1; i < 0.9; i += 0.1) {
      range.push(
        getThreeBezierPoint(i, [x, y], [x1, y1], [x2, y2], [endX, endY])
      );
    }
    range.push([endX, endY]);
    let startX1;
    let startY1;
    let endX2;
    let endY2;
    for (let i = 0; i < range.length - 1; i++) {
      startX1 = range[i][0];
      startY1 = range[i][1];
      endX2 = range[i + 1][0];
      endY2 = range[i + 1][1];
      let { minX, minY, maxX, maxY } = compare([
        [startX1, startY1],
        [endX2, endY2],
      ]);
      // 先判断当前点击位置是否在某一段的范围内（存在可能同时点中了多段区域，但是为了节约计算成本，如果已经找到了一个范围，则停止对后续范围的判断）
      if (
        clickPos.x > minX - 3 &&
        clickPos.x < maxX + 3 &&
        clickPos.y > minY - 3 &&
        clickPos.y < maxY + 3
      ) {
        let variable = lineEquation({
          x: startX1,
          y: startY1,
          endX: endX2,
          endY: endY2,
        });
        if (distanceToLine(variable, clickPos) < 6) {
          return curve;
        }
        return null;
      }
    }
    return null;
  }

  // 判断是否点击到图案的标记点
  judgeSign({ x, y }) {
    if (!this.sign.length) return;
    let getImage = this.backboard.getImageData(x, y, 1, 1).data;
    let rgb = `rgb(${getImage[0]},${getImage[1]},${getImage[2]})`;
    let ele = this.boardSign[rgb];
    if (ele) {
      return ele;
    } else {
      return null;
    }
  }

  // 判断当前点击点的另一端（传入当前点击的元素和标记点的坐标）
  getOtherEnd(ele, { x, y }) {
    if (ele.type === "line") {
      if (x === ele.endX && y === ele.endY) {
        return null;
      } else {
        return true;
      }
    }
  }

  // 获取标记点的中心点（旋转往往围绕这个点旋转）
  // 传入图案信息和图案的中心点坐标（originX, originY）
  getCenterPoint({ type, x, y, width, height }, { originX, originY }) {
    let center = { x: 0, y: 0 };
    if (["rect", "image"].includes(type)) {
      // 为矩形时，会考虑图案的旋转，在绘制时将原点设置成了图案的原点，所以x,y应为0,0点
      if (this.signElement) {
        // * 当图案被选中时， originX, originY 为被选中图案的中心点
        center = { x: originX, y: originY };
      } else {
        center = { x: x + width / 2, y: y + height / 2 };
      }
    } else if (type === "circular") {
      center = { x, y };
    }
    return center;
  }

  // 添加图形缩放框 （位于图形四个角，传入选中的图形）
  drawBorder(ele) {
    let { type, x, y, width, height, id, rotate } = ele;
    let signWidth = this.signWidth;
    let signHeight = this.signHeight;
    let { signX, signY } = this.getConnectInfo({
      type,
      x,
      y,
      signWidth,
      signHeight,
    });
    this.centerTarget = this.getCenterPoint(
      { type, x, y, width, height },
      { originX: this.originX, originY: this.originY }
    );
    if (["rect", "image"].includes(type)) {
      // 矩形缩放时，要固定中心点（否则旋转后缩放时，中心点发生改变会出现问题）
      signX = this.dragStartX - signWidth / 2 + this.subX;
      signY = this.dragStartY - signHeight / 2 + this.subY;
    } else if (type === "circular") {
      signX = -signWidth / 2;
      signY = -signHeight / 2;
    }
    let arr = ele.zoom
      ? this.drawBorderZoom({ ...ele, type, width, height, signX, signY })
      : [];
    let connect = ele.connect
      ? Object.values(
          this.drawBorderConnect({
            ...ele,
            signX,
            signY,
            centerX: this.centerTarget.x,
            centerY: this.centerTarget.y,
          })
        )
      : [];
    arr.push(...connect);
    this.sign = [];
    let signEle;
    for (let i = 0; i < arr.length; i++) {
      signEle = {
        signId: i,
        signWidth,
        signHeight,
        id,
        rotate,
        centerX: this.centerTarget.x,
        centerY: this.centerTarget.y,
      };
      Object.assign(signEle, arr[i]);
      this.drawSign(signEle);
      this.saveSignRect(signEle);
    }
    this.repaintBackboard();
  }

  // 获取 “实时” 连接点的位置信息
  getConnectInfo({ type, x, y, signWidth, signHeight }) {
    let signX = x - signWidth / 2;
    let signY = y - signHeight / 2;
    if (["rect", "image"].includes(type)) {
      // 为矩形时，会考虑图案的旋转，在绘制时将原点设置成了图案的原点，所以x,y应为0,0点
      signX = this.dragStartX - signWidth / 2 + this.subX;
      signY = this.dragStartY - signHeight / 2 + this.subY;
    } else if (type === "circular") {
      signX = -signWidth / 2;
      signY = -signHeight / 2;
    }
    return { signX, signY };
  }

  // 获取 “静态” 连接点的位置信息（区别于 ｜获取 “实时” 连接点｜ 的那个方法）
  getQuietConnectInfo({ type, signWidth, signHeight, width, height }) {
    let signX = 0;
    let signY = 0;
    if (["rect", "image"].includes(type)) {
      signX = -width / 2 - signWidth / 2;
      signY = -height / 2 - signHeight / 2;
    } else if (type === "circular") {
      signX = -signWidth / 2;
      signY = -signHeight / 2;
    }
    return { signX, signY };
  }

  // 判断是否点击中图形，并获取该图形的信息（如果有多个图形都在当前点击的坐标点内，则以获取到的第一个图形为准）
  // 如果点击的第一个图形为 线 ，则会判断是否点中该条线，如果没点中，则会继续判断是否选中后续的图案
  // 传入鼠标点击的点
  judge({ x, y }) {
    let getImage = this.backboard.getImageData(x, y, 1, 1).data;
    let rgb = `rgb(${getImage[0]},${getImage[1]},${getImage[2]})`;
    let eleId = this.boardCanvas[rgb];
    if (eleId) {
      let ele = this.canvasInfoMap.get(eleId);
      return ele && !ele.unClick ? ele : null;
    } else {
      // 可能正在尝试选中直线/曲线: 通过点到直线/曲线的距离判断
      let ele = [];
      if (this.canvasInfoMap.size) {
        ele = Array.from(this.canvasInfoMap).filter((i) => {
          return (
            (i[1].type === "line" &&
              x > i[1].minX - 3 &&
              x < i[1].maxX + 3 &&
              y > i[1].minY - 3 &&
              y < i[1].maxY + 3) ||
            ["connectCurve", "connectArrowCurveFill"].includes(i[1].type)
          );
        });
      }
      if (ele.length) {
        for (let i of ele) {
          if (i[1].type === "line") {
            // 直接判断点到直线的距离
            if (distanceToLine(i[1], { x, y }) < 6) {
              return i[1];
            }
          } else if (
            ["connectCurve", "connectArrowCurveFill"].includes(i[1].type)
          ) {
            // 判断点到曲线的距离
            let res = this.judgeCurve(i[1], { x, y }); // 内部处理了 是否选中 / 是否在曲线附近
            if (res) return res;
          }
        }
        return null;
      } else {
        return null;
      }
    }
  }

  // 缩放框坐标
  drawBorderZoom(ele) {
    let { type, radiusX, radiusY, endX, endY, width, height, signX, signY } =
      ele;
    let arr = [];

    let signWidth = this.signWidth;
    let signHeight = this.signHeight;
    if (["rect", "image"].includes(type)) {
      // if(this.signElement){
      //   this.centerTarget = {x:this.originX, y:this.originY};
      // }else{
      //   this.centerTarget = {x:x+width/2, y:y+height/2};
      // }

      // 矩形缩放时，要固定中心点（否则旋转后缩放时，中心点发生改变会出现问题）
      signX = this.dragStartX - signWidth / 2 + this.subX;
      signY = this.dragStartY - signHeight / 2 + this.subY;
    } else if (type === "circular") {
      // this.centerTarget = {x, y};
      signX = -signWidth / 2;
      signY = -signHeight / 2;
    }
    if (type === "line") {
      arr = [
        { signX, signY, signType: "line" },
        {
          signX: endX - signWidth / 2,
          signY: endY - signHeight / 2,
          signType: "line",
        },
      ];
    }
    if (["rect", "image"].includes(type)) {
      // pos: 矩形的四个角，从左上角顺时针分别为1234
      arr = [
        { signX, signY, pos: "1" },
        { signX: signX + width, signY, pos: "2" },
        { signX: signX + width, signY: signY + height, pos: "3" },
        { signX: signX, signY: signY + height, pos: "4" },
      ];
      if (height >= 0) {
        arr.push({
          signX: signX + width / 2,
          signY: signY - 15,
          signType: "rotate",
        });
      } else {
        arr.push({
          signX: signX + width / 2,
          signY: signY + 15,
          signType: "rotate",
        });
      }
    }
    if (type === "circular") {
      // 根据圆的某一个面来渲染
      // 1. 先获取到圆周边的坐标
      let pos = getCircularPos({
        startX: ele.startX || 0,
        startY: ele.startY || 0,
        radiusX,
        radiusY,
        startAngle: ele.startAngle,
        endAngle: ele.endAngle,
      });
      // 2. 根据 1 中的坐标得出圆的四个顶点
      let { minX, minY, maxX, maxY } = compare(pos);
      // arr = [{signX:signX-radiusX,signY:signY-radiusY,pos:'1'}, {signX:signX+radiusX,signY:signY-radiusY,pos:'2'}, {signX:signX+radiusX,signY:signY+radiusY,pos:'3'}, {signX:signX-radiusX,signY:signY+radiusY,pos:'4'}];
      arr = [
        { signX: signX + minX, signY: signY + minY, pos: "1" },
        { signX: signX + maxX, signY: signY + minY, pos: "2" },
        { signX: signX + maxX, signY: signY + maxY, pos: "3" },
        { signX: signX + minX, signY: signY + maxY, pos: "4" },
      ];
      arr.push({
        signX: signX + (minX + maxX) / 2,
        signY: signY + minY - 15,
        signType: "rotate",
      });
    }
    return arr;
  }

  // 连接点的坐标
  // signX / signY: 当前图案第一个矩形标记点的坐标
  drawBorderConnect(ele) {
    let {
      type,
      width,
      height,
      signX,
      signY,
      rotate,
      radiusX,
      radiusY,
      x,
      y,
      centerX,
      centerY,
    } = ele;
    let connect = {};
    let rulerX = 0;
    let rulerY = 0;
    if (["rect", "image"].includes(type)) {
      rulerX = width / 2;
      rulerY = height / 2;
      // 连接线标记点
      connect = {
        1: {
          pos: "1",
          signX: signX + rulerX,
          signY: signY,
          x: signX + rulerX,
          y: signY,
          signType: "connect",
          ...this.getRealPos({
            x: centerX,
            y: centerY,
            rotate,
            signCenterX: x + rulerX,
            signCenterY: y,
          }),
        },
        2: {
          pos: "2",
          signX: signX + width,
          signY: signY + rulerY,
          x: signX + width,
          y: signY + rulerY,
          signType: "connect",
          ...this.getRealPos({
            x: centerX,
            y: centerY,
            rotate,
            signCenterX: x + width,
            signCenterY: y + rulerY,
          }),
        },
        3: {
          pos: "3",
          signX: signX + rulerX,
          signY: signY + height,
          x: signX + rulerX,
          y: signY + height,
          signType: "connect",
          ...this.getRealPos({
            x: centerX,
            y: centerY,
            rotate,
            signCenterX: x + rulerX,
            signCenterY: y + height,
          }),
        },
        4: {
          pos: "4",
          signX: signX,
          signY: signY + rulerY,
          x: signX,
          y: signY + rulerY,
          signType: "connect",
          ...this.getRealPos({
            x: centerX,
            y: centerY,
            rotate,
            signCenterX: x,
            signCenterY: y + rulerY,
          }),
        },
      };
    }
    if (type === "circular") {
      // 连接线标记点
      let pos = getCircularPos({
        startX: ele.startX || 0,
        startY: ele.startY || 0,
        radiusX,
        radiusY,
        startAngle: ele.startAngle,
        endAngle: ele.endAngle,
      });
      let { minX, minY, maxX, maxY } = compare(pos);
      connect = {
        1: {
          pos: "1",
          signX: (minX + maxX) / 2 + signX,
          signY: minY + signY,
          x: signX,
          y: signY - radiusY,
          signType: "connect",
          ...this.getRealPos({
            x,
            y,
            rotate,
            signCenterX: x + (minX + maxX) / 2,
            signCenterY: y + minY,
          }),
        },
        2: {
          pos: "2",
          signX: maxX + signX,
          signY: (minY + maxY) / 2 + signY,
          x: signX + radiusX,
          y: signY,
          signType: "connect",
          ...this.getRealPos({
            x,
            y,
            rotate,
            signCenterX: x + maxX,
            signCenterY: y + (minY + maxY) / 2,
          }),
        },
        3: {
          pos: "3",
          signX: (minX + maxX) / 2 + signX,
          signY: maxY + signY,
          x: signX,
          y: signY + radiusY,
          signType: "connect",
          ...this.getRealPos({
            x,
            y,
            rotate,
            signCenterX: x + (minX + maxX) / 2,
            signCenterY: y + maxY,
          }),
        },
        4: {
          pos: "4",
          signX: minX + signX,
          signY: (minY + maxY) / 2 + signY,
          x: signX - radiusX,
          y: signY,
          signType: "connect",
          ...this.getRealPos({
            x,
            y,
            rotate,
            signCenterX: x + minX,
            signCenterY: y + (minY + maxY) / 2,
          }),
        },
      };
    }
    return connect;
  }

  // 计算画布中图案（矩形/圆形）连接点的坐标
  getAllLinks() {
    let arr = [];
    this.linkArr = [];
    let signRadius = 3;
    let centerX;
    let centerY;
    let group = [];
    let linkEle;
    if (this.canvasInfoMap.size)
      group = Array.from(this.canvasInfoMap).filter(
        (i) => !["connectCurve"].includes(i[1].type) && i[1].connect
      );
    for (const [key, ele] of group) {
      let { id, type, width, height, x, y } = ele;
      if (id === this.ele.id || type === "line") continue;
      if (["rect", "image"].includes(type)) {
        // 连接线标记点
        centerX = x + width / 2;
        centerY = y + height / 2;
        ele.centerX = centerX;
        ele.centerY = centerY;
        ele.signX = -width / 2 - signRadius;
        ele.signY = -height / 2 - signRadius;
        arr = Object.values(this.drawBorderConnect(ele));
      }
      if (type === "circular") {
        centerX = x;
        centerY = y;
        ele.centerX = centerX;
        ele.centerY = centerY;
        ele.signX = -signRadius;
        ele.signY = -signRadius;

        arr = Object.values(this.drawBorderConnect(ele));
      }
      this.canvasInfoMap.set(key, ele);
      for (let i = 0; i < arr.length; i++) {
        linkEle = {
          signId: i + 1000,
          id,
          signWidth: signRadius * 2,
          signHeight: signRadius * 2,
          centerX,
          centerY,
          x: arr[i].signX,
          y: arr[i].signY,
        };
        Object.assign(linkEle, arr[i]);
        this.linkArr.push(linkEle);
        this.saveSignRect(linkEle);
      }
    }
  }
  // 绘制所有图形的连接点
  drawGetAllLinks() {
    for (let i of this.linkArr) {
      this.drawSign(i);
    }
  }

  // 计算标记点坐标，并保存
  // type: zoom:缩放  rotate:旋转
  saveSignRect({
    id,
    signId,
    signX,
    signY,
    signWidth,
    signHeight,
    rotate = 0,
    signType = "zoom",
    pos,
    signCenterX,
    signCenterY,
    originSignX,
    originSignY,
  }) {
    let minX = signX;
    let minY = signY;
    let maxX = signX + signWidth;
    let maxY = signY + signHeight;
    let obj = {
      id,
      rotate,
      pos,
      signType,
      x: signX + signWidth / 2,
      y: signY + signHeight / 2,
      signId,
      signX,
      signY,
      signWidth,
      signHeight,
      minX,
      minY,
      maxX,
      maxY,
      endX: 0,
      endY: 0,
      signCenterX,
      signCenterY,
      originSignX,
      originSignY,
    };
    this.sign.unshift(obj);
  }
  // 绘制标记点
  drawSign({
    signX,
    signY,
    signWidth,
    signHeight,
    rotate = 0,
    signType = "zoom",
    radius = 3,
    centerX,
    centerY,
  }) {
    this.ctx.lineWidth = 1;
    if (["zoom", "rotate", "connect"].includes(signType)) {
      this.ctx.save();
      this.ctx.translate(centerX, centerY); // 重新定义当前坐标系原点
      this.ctx.rotate(rotate);
      if (signType === "connect") {
        this.ctx.fillStyle = this.defaultSignStyle.connectColor;
        this.ctx.beginPath();
        this.ctx.arc(
          signX + signWidth / 2,
          signY + signHeight / 2,
          radius,
          0,
          Math.PI * 2,
          true
        );
        this.ctx.fill();
      } else {
        if (signType === "zoom") {
          this.ctx.strokeStyle = this.defaultSignStyle.zoomColor;
          this.ctx.lineWidth = this.defaultSignStyle.zoomWidth;
        } else {
          this.ctx.strokeStyle = this.defaultSignStyle.rotateColor;
          this.ctx.lineWidth = this.defaultSignStyle.rotateWidth;
        }
        this.ctx.strokeRect(signX, signY, signWidth, signHeight);
      }
      this.ctx.restore();
    } else {
      // 直线不需要旋转坐标
      this.ctx.strokeRect(signX, signY, signWidth, signHeight);
    }
  }

  // 计算标记点中心旋转后的真实坐标   传入图形中心坐标和标记点中心的真实坐标
  getRealPos({ x, y, rotate, signCenterX, signCenterY }) {
    let { realX, realY } = getReal({
      x: signCenterX - x,
      y: y - signCenterY,
      rotate,
    });
    return {
      signCenterX: realX + x,
      signCenterY: y - realY,
      rotate,
      originSignX: signCenterX,
      originSignY: signCenterY,
    };
  }

  /**
   * 固定图案的中心点和初始的xy
   *  - 矩形缩放固定中心点
   *  - 画布禁止拖拽时，点击节点后固定中心点（防止 reload 时候矩形位置发生变化）
   * @param {Object} ele  完整的节点信息
   */
  fixedCenterPoint(ele) {
    if (!ele) return;
    if (["rect", "image"].includes(ele.type)) {
      this.startCenterX = ele.x + ele.width / 2;
      this.startCenterY = ele.y + ele.height / 2;
      this.dragStartX = -ele.width / 2;
      this.dragStartY = -ele.height / 2;
      // 固定中心点
      this.originX = ele.x + ele.width / 2;
      this.originY = ele.y + ele.height / 2;
    } else if (ele.type === "circular") {
      this.originX = ele.x;
      this.originY = ele.y;
    }
  }

  // 计算图案的中心点
  getCanvasCenterPoint(ele) {
    let originX;
    let originY;
    if (["rect", "image"].includes(ele.type)) {
      // 固定中心点
      originX = ele.x + ele.width / 2;
      originY = ele.y + ele.height / 2;
    } else if (ele.type === "circular") {
      originX = ele.x;
      originY = ele.y;
    }
    return { originX, originY };
  }
}
